package io.nads.service.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.nads.constant.RedisConstant;
import io.nads.context.BaseContext;
import io.nads.dto.Result;
import io.nads.entity.User;
import io.nads.mapper.UserMapper;
import io.nads.service.IUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    private final StringRedisTemplate stringRedisTemplate;

    @Override
    public Result register(User user) {
        // 判断username、email、password、code是否为空
        if (StrUtil.isBlank(user.getUsername()) ||
                StrUtil.isBlank(user.getEmail()) ||
                StrUtil.isBlank(user.getPassword()) ||
                user.getCode() == null) {
            return Result.fail("用户名、邮箱、密码、验证码均不能为空");
        }
        // 判断用户名中是否含有特殊字符
        if (!user.getUsername().matches("^[a-zA-Z0-9_]+$")) {
            log.info("用户用于注册的username不合法 {}", user.getUsername());
            return Result.fail("用户名包含特殊字符");
        }
        // 判断验证码是否正确
        String codeStr = stringRedisTemplate.opsForValue().get(RedisConstant.USER_EMAIL_CODE_KEY_PREFIX + user.getEmail());
        if (StrUtil.isBlank(codeStr)) {
            return Result.fail("验证码未发送或已过期");
        }
        Long code = Long.valueOf(codeStr);
        if (!code.equals(user.getCode())) {
            return Result.fail("验证码错误");
        }
        // 判断数据库里面是否已经有用户
        // TODO redis缓存
        if (lambdaQuery().eq(User::getUsername, user.getUsername()).count() > 0) {
            return Result.fail("用户名已存在");
        }
        // 对密码进行加密
        String hashedPassword = BCrypt.hashpw(user.getPassword());
        user.setPassword(hashedPassword);
        // 保存用户
        save(user);
        return Result.ok("注册成功");
    }

    @Override
    public Result sendCode(String email) {
        // 判断邮箱是否合法
        if (!Validator.isEmail(email)) {
            return Result.fail("邮箱格式错误");
        }
        // 判断邮箱是否已被注册
        // TODO redis缓存
        if(lambdaQuery().eq(User::getUsername, email).count() > 0) {
            return Result.fail("该邮箱已被注册过");
        }
        // 判断redis中是否已经存在code且距离上次生成时间不超过60s
        Long expire = stringRedisTemplate.getExpire(RedisConstant.USER_EMAIL_CODE_KEY_PREFIX + email);
        if(expire != null && RedisConstant.USER_EMAIL_CODE_EXPIRE - expire < 60) {
            log.warn("用户频繁获取验证码");
            return Result.fail("请勿频繁获取验证码");
        }
        // 生成6位随机密码
        int code = (int) (Math.random() * 900000 + 100000);
        // 保存到redis
        stringRedisTemplate.opsForValue().set(RedisConstant.USER_EMAIL_CODE_KEY_PREFIX + email,
                String.valueOf(code), RedisConstant.USER_EMAIL_CODE_EXPIRE, TimeUnit.SECONDS);
        // TODO 发送验证码逻辑
        log.info("发送验证码 {} 到邮箱 {}", code, email);
        return Result.ok("验证码发送成功，" + RedisConstant.USER_EMAIL_CODE_EXPIRE / 60 + "分钟内有效");
    }

    @Override
    public Result login(User user) {
        // 查询用户
        User dbUser = lambdaQuery().eq(User::getUsername, user.getUsername()).one();
        if (dbUser == null) {
            // 邮箱查询
            dbUser = lambdaQuery().eq(User::getEmail, user.getUsername()).one();
            if (dbUser == null) {
                log.warn("用户不存在 {}",  user);
                return Result.fail("用户不存在");
            }
        }
        // 校验密码
        if (!BCrypt.checkpw(user.getPassword(), dbUser.getPassword())) {
            log.warn("密码错误 {}",  user);
            return Result.fail("密码错误");
        }
        // 检查ThreadLocal中是否有用户id
        Long userId = BaseContext.getCurrentId();
        if (userId != null) {
            log.warn("用户已经登录");
            return Result.fail("您已登录，无需重复登录");
        }
        // 返回token
        String token = UUID.randomUUID().toString();
        log.debug("生成token {}", token);
        // 缓存用户信息
        User cachedUser = User.builder().id(dbUser.getId()).username(dbUser.getUsername()).build();
        String jsonStr = JSONUtil.toJsonStr(cachedUser);
        stringRedisTemplate.opsForValue().set(RedisConstant.USER_TOKEN_KEY_PREFIX + token, 
            String.valueOf(jsonStr), RedisConstant.USER_TOKEN_EXPIRE, TimeUnit.SECONDS);
        // 查询用户最近一次的会话
        log.info("用户 {} 登录", user.getUsername());
        return Result.ok(token);
    }

    @Override
    public Result logout(String token) {
        log.info("用户{} 注销 token = {}", BaseContext.getCurrentId(), token);
        Boolean delete = stringRedisTemplate.delete(RedisConstant.USER_TOKEN_KEY_PREFIX + token);
        if (delete) {
            return Result.ok("注销成功");
        }
        else {
            return Result.fail("未知错误");
        }
    }

    @Override
    public Result getUserByToken(String token) {
        String userJsonStr = stringRedisTemplate.opsForValue().get(RedisConstant.USER_TOKEN_KEY_PREFIX + token);
        User user = JSONUtil.toBean(userJsonStr, User.class);
        return Result.ok(user);
    }
}
